// 组合式API


// setup概述
// setup是vue3中一个新的配置项，值是一个函数，他是Composition API “表演的舞台”
// 组件中所用的：数据，方法，计算属性，监视...等等，均配置在setup中
// 特点：
// setup函数返回的对象中的内容，可直接在模板中使用
// setup中访问this是undefined
// setup函数会在beforeCreate之前调用，他是“领先”所有钩子执行的

/* ---------------------响应式数据--------------------------------- */

/* ref(基本类型的响应式数据，也可以给对象使用)
作用：定义响应式数据
语法：let xxx = ref(初始值)
返回值：一个RefImpl的实例对象，简称ref对象，ref对象的value属性是响应式的
注意点：
js中操作数据需要：xxx.value，但模版中不需要value，直接使用即可
 */


/* reactive(对象类型数据)
语法：let xxx = reactive({})
返回值：reactive()的返回值是一个 ​​Proxy 代理对象​​，
通过拦截属性操作实现深层响应式。使用时需注意避免直接替换对象或解构属性，推荐结合 toRefs()处理解构场景
不能整体修改
*/

/* ref和reactive的区别
ref创建的变量必须使用.value
reactive重新分配一个新对象，会失去响应式（可以还是用Object.assign去整体替换）（即重构对象）
例如：
let obj = reactive({
    a: 1,
    b: 2
})
// 若直接修改obj，会失去响应式
obj = {
    a: 3,
    b: 4
}
// 若要整体修改obj，推荐使用Object.assign
Object.assign(obj, {
    a: 3,
    b: 4
})
使用原则：
若需要一个基本类型的响应式数据，必须使用ref
若需要一个响应式对象，层级不变，ref，reactive都可以
若需要一个响应式对象，切层级较深，推荐使用reactive
*/


/* toRefs与toRef
作用：将一个响应式对象终端每一个属性，转换为ref对象(对象属性也会变)
注意：toRefs与toRef功能一致，但toRefs可以批量转换
语法：
let obj = reactive({
    a: 1,
    b: 2
})
let objRefs = toRefs(obj)
// 现在objRefs.a和objRefs.b都是ref对象，且与obj.a和obj.b保持响应式联系
*/


/* v-model与v-model
在 HTML 的 <input>元素中，你几乎总是应该使用 ​​v-model​​ 而不是 v-model:value。
​​v-model​​ 是 Vue 提供的语法糖，用于简化表单输入元素的双向数据绑定，它会根据输入类型自动选择正确的属性和事件
​​v-model:value​​ 这种形式主要用于自定义 Vue 组件，以显式指定要绑定的属性名（例如 value），尤其在需要处理多个可双向绑定的 prop 时
对于原生输入元素，放心使用 v-model即可。当基于 Vue 封装自定义表单组件时，才会用到 v-model:xxx这样的语法来提供更灵活的双向绑定
 */


/* computde计算属性
computed属性是 Vue ​​响应式系统​​的一部分。它通过​​依赖追踪​​机制自动收集计算过程中使用的响应式数据作为依赖。当这些依赖发生变化时，计算属性会​​自动重新求值​​。
如果依赖没有变化，则总是返回之前缓存的结果，这是其高效的关键


​​避免副作用​​：计算属性应专注于计算并返回一个值，不要在其中执行异步操作或修改其他数据（副作用）。副作用的操作应使用 watch或方法 methods
​​不可直接修改​​：默认情况下，计算属性是只读的（仅 getter）。如果你需要可写的计算属性，必须提供 setter 函数，并在 setter 中修改其依赖的数据
​​依赖追踪限制​​：计算属性仅会追踪其​​同步执行期间​​访问的响应式依赖。如果在计算属性内部使用了异步回调（如 setTimeout、Promise）并在回调中访问了响应式数据，这些数据将不会被正确追踪为依赖
*/


/* ----------------------------------------- watch--------------------------------- */
/* watch
作用：监视数据的变化
特点：vue3中watch只能监视以下4种数据
ref定义的数据(不是value)
reactive定义的数据
函数返回一个值
一个包含上述内容的数组
语法：
watch(监视数据名,回调函数,watch配置对象)
其中第三个参数是用来配置的当监视的是对象时，可以输入{deep:true}来开启深度监视（监视对象数据变化）

注意：watch函数有返回函数，返回函数用于结束监视（具体使用在备份中）
使用场景：
1.监视ref定义的（基本类型）数据：直接写数据名即可，监视的是其value值的变化

2.监视ref定义的（对象类型）数据，直接写数据名，监视的是对象的地址值，若想监视对象内部的数据，要手动开启深度监视
注意：
若修改的是ref定义的对象中的属性，newValue和oldValue都是新值,因为他们是同一个对象（对属性修改）
若修改整个ref定义的对象，newValue是x新值，oldValue是旧值，因为不是一个对象了（对对象修改）

3.监视reactive定义的对象类型数据，且默认开起来深度监视
即当对象是reactive定义的时，使用watch监视会默认开启深度监视，且不可关闭

4.监视ref或reactive定义的（对象类型）数据中的某个属性
注意点：
（1）若该属性值不是对象类型，需要写成函数形式
（2）若该属性值是依然是对象类型，可直接编，也可写成函数，不过建议写成函数(这里有一个问题，你监视对象内部就无法监视对象整体，所以
要想监视内部又想监视整体，需要手动开启深度监视)

5.监视上述的多个数据
使用举例在备份4中，事实上适不适用箭头函数是取决于你要监视的是谁，也对应情况4的条件


在实际开发中，情况1和4使用比较多，因为函数稳定
*/



/* ---------------------------watchEffect------------------------------- */

/* watchEffect
作用：立即运行一个函数，同事响应式地追踪其依赖，并在依赖更改时重新执行该函数
watch和watchEffect的对比
1.都能监听响应式数据的变化，不同都是监听数据变化的方式不同
2.watch：要明确指出监视的数据
3.watchEffect：不用明确指出监视的数据（函数中用到哪些属性那就监视哪些属性）
示例：
watchEffect(() => {
    console.log(obj.a)
})
其中：obj.a是响应式数据，当obj.a变化时，会触发watchEffect函数重新执行，并打印obj.a
*/

/* ------------------------------------标签的ref属性-------------------------- */


/* 标签的ref属性
作用：用于注册模版引用
1.用在普通DOM标签上，获取的是DOM节点
用在组件标签上，获取的是组件实例对象

例如：<h1 ref = "test"> hello </h1>
let test = ref(1)
*/


/* --------------------propa的使用（组件通信）------------------------- */
/* 在 Vue 3 中，​​props（属性）​​ 是组件之间数据传递的核心机制。它的作用是​​让父组件可以向子组件传递数据​​。
传输语法
语法：<组件名 :属性名 = "属性值" />
1.接收语法
defineProps(['a', 'b', 'c'....])
2.接收+保存语法(x是对象类型)
let x = defineProps(['a', 'b', 'c'....])
3.接收+限制类型
defineProps<a:泛型API>()
4接收+限制类型+限制必要性+指定默认值
withDefaults(defineProps<{a?:泛型API}>(), {
  a:() => [{泛型API的内容}]
})
其中?:是指父组件可传可不传，当不传时使用默认值
*/


/* -------------------------生命周期----------------------------------- */

/* 生命周期
组件的生命周期：
创建
挂载
更新
销毁
别名：生命周期，生命周期函数，生命周期钩子
*/

/* -------------------------hooks----------------------------------- */

/* hooks
作用：将组件中重复使用的逻辑提取出来，封装成一个函数，方便组件调用
命名约定：自定义 Hooks 的函数名通常以 ​​use​​ 开头（例如 useCart, useSearch），这是一个公认的约定，便于快速识别
清理副作用：如果 Hook 中设置了事件监听器、定时器等副作用，务必在 onUnmounted生命周期钩子中进行清理，防止内存泄漏

见解：Vue 3 中的 Hooks，也称为组合式函数，是组合式 API（Composition API）的核心组成部分。
它允许你将组件的响应式状态和逻辑封装成可复用的函数，从而更好地组织代码并实现高效的逻辑复用 
*/


/* -------------------------路由------------------------------ */
/* 路由
定义：路由是指根据请求的URL路径，将请求分发到不同的处理函数或组件的机制。
vue中的路由作用：
1.实现单页应用（SPA）：路由允许你在不刷新页面的情况下，通过改变 URL 路径来加载不同的组件，从而实现页面的切换和导航。
2.路由参数：路由可以定义动态参数，例如 /user/:id，其中 :id 是一个参数占位符。当用户访问 /user/123 时，路由可以将 123 提取出来作为参数传递给组件。
3.路由守卫：路由守卫是指在路由切换过程中执行的一些函数，用于控制路由的访问权限、进行路由跳转前的确认等。Vue 提供了多种路由守卫，例如 beforeEach、afterEach 等。
4.路由懒加载：路由懒加载是指在需要的时候才加载路由对应的组件，而不是在应用初始化时就加载所有组件。这可以提高应用的加载速度和性能。

详情在index.ts中

两个注意点
1.路由组件通常放在pages或views文件夹，一般组件通常存放在components文件夹
2.通过点击导航，视觉效果上“消失”了的路由组件，默认是被“卸载”掉的，需要的时候再去挂载




to的两种写法
1.to的字符串写法
<router-link to="/home">Home</router-link>
2.to的object写法
<router-link :to="{path:'/home'}">Home</router-link>

路由器的工作模式
history模式
优点：URL更加美观，不带有#，更接近传统的网址URL
缺点：后期项目上线《需要服务器端配合处理路径问题，否则刷新会有404错误
vue2：mode:'history'
vue3：history:createWebHistory()
React：BrowserRouter

hash模式
有点：兼容性更好，因为不需要服务器端处理路径
缺点：URL带有#不美观，且在SED优化方面相对较差
vue2：mode:'hash'
vue3：history:createWebHashHistory()

嵌套路由

query参数

params参数

props配置

replace属性

编程式路由导航

重定向
------------------------------------------------暂且跳过*/


































/* -------------------------pinia------------------------------ */

/* pinia
Pinia 是 Vue.js 的官方推荐的状态管理库，特别为 Vue 3 设计，提供了一种简洁、类型安全的方式来管理应用中的全局状态
它为你提供了一个集中式的存储空间（称为 "Store"）来管理应用中需要跨组件共享的数据
使用步骤：
1.安装并在main.js中引入pinia
import { createPinia } from 'pinia'
const pinia = createPinia()
app.use(pinia)

2.定义store
import { defineStore } from 'pinia'
const useUserStore = defineStore('user', {
  state: () => ({
    name: '张三',
    age: 18
  }),
  actions: {
    changeName(newName) {
      this.name = newName
    }
  }
})

3.在组件中引入并使用store
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
// 访问状态
console.log(userStore.name) // 输出：张三
console.log(userStore.age) // 输出：18 

4.符合直觉的修改方式
直接修改：
userStore.name = '李四'

通过$patch()方法修改($patch()方法接受一个对象或函数)：
userStore.$patch({
  name: '王五',
  age: 20
})

通过actions修改（在store中定义的方法）（比较核心的修改方式，可以实现复杂的业务逻辑（转移业务逻辑，增加可读性）：
userStore.changeName('赵六')
*/


/* storeToRefs
作用：将 store 中的状态转换为响应式的 ref 对象，方便在组件中使用
storeToRefs只会关注sotre中的数据，不会对方法发进行ref包裹
*/


/* getters
概念：当state中的数据，需要经过处理后再使用时，可以使用getters配置
*/
//以上storeToRefs和getters的语法自行了解
/* -------------------------组件通信------------------------------ */
// 具体代码在vue3学习资料文件夹中
/* 使用props来通信
父穿子语法：
<子组件名 :属性名 = "属性值" />
子接受父语法（不止一种与语法）：
defineProps(['属性名']) 
子传父语法(需要父先定义一个函数，子组件调用这个函数并传递参数)：
<子组件名 @事件名="方法名" />

在父传子时：属性值是非函数
在子传父时：属性值是函数
props不要在多层级间使用
*/

/* 自定义事件（子传父）
父组件语法（给自组件绑定事件）
<子组件名 @事件名="方法名" />

子组件语法
const emit = defineEmits(['事件名'])
emit('事件名', 参数)
其中名称emit是规范
*/

/* mitt
在 Vue 3 中，mitt之所以被称为“事件总线库”，是因为它的工作模式很像一条在应用程序中穿梭的“数据总线”
为各个组件提供了一个全局的、集中化的事件通信机制
基本概念概述：
1.全局中心：在使用props和自定义事件进行组件通信时，在兄弟组件或层级较深的组件通信时常常无力且繁琐，mitt
旨在通过一个事件中心来实现组件之间的通信，避免了直接在组件之间传递数据的复杂性。
2.发布和订阅的简单模式：组件通过mitt实例的on方法订阅事件，通过emit方法发布事件。

使用步骤：
1.创建：
另外创建一个工具文件夹（hooks或utils），在其中创建一个mitt.ts文件(规范)
创建mitt实例：
import mitt from 'mitt'    // 引入mitt库
const mittBus = mitt()    // 创建mitt实例
export default mittBus    // 导出mitt实例
2.使用：
在需要通信的组件中引入mitt实例：
import mittBus from '@/utils/mitt'
// 订阅事件
mittBus.on('事件名', (参数) => {
  console.log('事件触发了', 参数)
})
// 发布事件
mittBus.emit('事件名', '参数')


3.卸载
在组件销毁时，需要卸载事件监听器，避免内存泄漏：
mittBus.off('事件名', 事件处理函数)
*/


/* v-model
在 Vue 3 中，v-model在组件上的使用相当于同时传递一个 prop 和绑定一个自定义事件

父组件
<ChildComponent v-model="message" />

等价于
<ChildComponent 
  :modelValue="message" 
  @update:modelValue="newValue => message = newValue" 
/>
其中update:modelValue是一个完整的事件名，包含了事件的名称和参数

子组件部分：
defineProps(['modelValue']) //prop
defineEmits(['update:modelValue'])  // 自定义事件
<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />  // 简单应用
其中$event：
对于原生事件：$event就是事件对象能target
对于自定义事件：$event是触发事件时传输的数据不能target



说白了：v-model把props和自定义事件和在一起了
应用场景：
紧密关联的父子组件
封装UI组件
追求简单直接：复合vue官方推荐的数据规范
*/


/* #attrs
$attrs是一个响应式对象，它包含了父组件传递给当前组件
但当前组件没有在props中显式声明的所有属性（包括自定义属性和事件监听器


内容来源：当你给一个子组件传递属性，如果子组件没有通过defineProps接收这些属性，那么它们就会被收集到$attrs对象中
Vue 3 的重要变化：在Vue 2中，普通的HTML属性（如class、style）和事件监听器是分开处理的（事件在$listeners对象里）。
而在Vue 3中，事件监听器也被合并到了$attrs中，并且class和style不再包含在$attrs内
。这使得属性透传更加简单统一，只需要操作$attrs即可

基本用法：
祖组件：<父组件名 :属性名 = "属性值" />
父组件：<子组件名 v-bind="$attrs" />
子组件：defineProps(['属性名'])
*/


/* $refs 和 $parent
$refs:
用于：父传子
访问方式：
<子组件名 ref="子组件名" />
在this.$refs或ref变量中访问子组件
子组件需要通过defineExpose暴露属性
使用场景：调用子组件方法、操作 DOM、批量处理子组件
示例：
父组件：
<template>
  <div>
    <子组件名 ref="子组件名" />
    <button @click="调用子组件方法">调用子组件方法</button>
  </div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const 子组件名 = ref(null)
onMounted(() => {
  console.log(子组件名.value.message)  // 访问子组件暴露的属性
})
function 调用子组件方法() {
  子组件名.value.方法名()  // 调用子组件暴露的方法
}
</script>


---------------------------
$parent:
用于：子传父
访问方式：
在子组件中通过this.$parent访问
父组件需要通过defineExpose暴露属性
示例：
子组件：
<template>
  <div>
    <h1>{{ message }}</h1>
  </div>
</template>
<script setup>
defineProps(['message'])
defineExpose({
  message: 'hello'
})
</script>
*/


/* slot
默认插槽：
父组件：
<子组件名>
  内容
</子组件名>
子组件：
<template>
  <div>
    <slot></slot>
  </div>
</template>


具名插槽：
父组件：（结构对应）
<子组件名>
  <template v-slot:插槽名>
    内容
  </template>
</子组件名>
子组件：
<template>
  <div>
    <slot name="插槽名"></slot>
  </div>
</template>

注意：
v-slot的简单写法是：#插槽名


作用域插槽：
父组件：（结构对应）
<子组件名>
  <template v-slot:插槽名="params">   params仅仅是名称，可以自定义，由子组件传输的数据都包含在这个“对象”中
    {{ params.属性名 }}
  </template>
</子组件名>
子组件：
<template>
  <div>
    <slot name="插槽名" :属性名="属性值"></slot>
  </div>
</template>

使用场景：
数据在子哪里，但根据数据生成的结构，却由父组件决定，通过作用域插槽，将数据的作用域延申到父组件

注意点：
1.v-slot:name 中的name是插槽的名称，必须和子组件中<slot name="name">的name一致
2.default是默认插槽的名称，在父组件中可以不写，直接写<template v-slot="params">
3.#default是默认插槽的简写，在父组件中可以不写，直接写<template #default="params">
*/


/* ----------------------------其他API---------------------------- */


/* shallowRef与shallowReactive
shallowRef
作用：创建一个响应式数据，但只对顶层属性进行响应式处理
用法：
let myVar = shallowRef(initialValue)
特点：只跟踪引用值的变化，不关心值内部的属性变化
应用场景：关注整体数据的变化，而不关心内部属性的变化

shallowReative
作用：创建一个浅层响应式对象，只会有使对象的最顶层属性变成响应式的
对象内部的嵌套属性则不会变成响应式的
用法：
const myObj = shallowReactive({....})
特点：对象的顶层属性是响应式的，但嵌套对象的属性不是

总结：
通过使用shallowRef()和shallowReactive()来绕开深度响应，浅层式API创建的状态只在其顶层是响应式的，对所有深层的对象不会做任何处理
避免了对每一个内部属性做响应式所带来的性能成本，这使得属性的访问变得更快，可提升性能
*/


/* readonly和shallowReadonly
readonly
作用：用于创建一个对象的深只读副本
用法：const original = reactive({...})
const readonlyCopy = readonly(original)
特点：
对象的所有嵌套属性都将变为只读
任何尝试修改这个对象的操作都会被阻止（开发模式下，会在控制台发出警告）
应用场景：
保护数据不被意外修改
在组件中传递只读数据，防止子组件修改父组件的数据

shallowReadonly
作用：与readonly类似，但只作用与对象的顶层属性
用法：
const original = reactive({...})
const shallowReadonlyCopy = shallowReadonly(original)
特点：
只将对象的顶层属性设置为只读，对象内部的嵌套属性仍然是可变的
适用于只需保护对象顶层属性的场景
*/ 



/* toRaw和markRaw
toRaw
作用：用于获取一个响应式对象的原始对象，toRaw返回的对象不再是响应式的，不会触发视图更新

官网描述：这是一个可以用于临时读取而不引起代理访问/跟踪开销
或是写入而不触发更改的特殊方法，不建议保存对原始对象的持久引用，请谨慎使用

使用场景：在需要将响应式对象传递给非vue的库或外部系统时，使用toRaw可以确保他们收到的是不同对象

markRaw
作用：用于标记一个对象，使其成为“非响应式的”
用法：
const obj = { ... }
markRaw(obj)
特点：
标记后的对象不会被转换为响应式代理
任何对该对象的操作都不会触发视图更新
应用场景：
当你有一个对象，它的属性不会被vue的响应式系统所需要处理时，使用markRaw可以避免性能开销
*/



/* customRef
作用：创建一个自定义的ref，并对其依赖项跟踪和更新触发进行逻辑控制
核心概念：customRef的使用围绕一个工厂函数。这个函数接收两个关键参数：track（用于依赖追踪）和 trigger（用于触发更新）。
函数必须返回一个包含 get和 set方法的对象

get()方法：当读取 ref的 .value时被调用。
你需要在其中返回当前值，并且必须调用 track()​ 函数，以便 Vue 知道当前有地方依赖这个数据

set(newValue)方法：当修改 ref的 .value时被调用。
你需要在其中更新实际的值，并且在值真正改变后必须调用 trigger()​ 函数，以通知所有依赖的地方进行更新

示例：
import { customRef } from 'vue';

function myRef(value) {
  return customRef((track, trigger) => {
    return {
      get() {
        track(); // 告诉Vue追踪依赖
        return value;
      },
      set(newValue) {
        value = newValue; // 更新值
        trigger(); // 告诉Vue触发更新
      }
    };
  });
}

*/


/* Teleport
Vue 3 中的 Teleport是一个内置组件，它允许你将组件模板的一部分“传送”到 DOM 树中的其他位置，
这在处理需要突破当前组件层级限制的 UI 元素时（如模态框、通知提示）非常有用

基本语法：
​
<Teleport to="目标选择器">...</Teleport>
不改变组件间的父子逻辑关系，props、事件、插槽、依赖注入均正常工作
支持动态目标（:to）、条件禁用（:disabled）、多组件同目标

使用场景：
在没有 Teleport的情况下，如果子组件（如一个全屏模态框）在逻辑上属于父组件，
那么它默认会被渲染在父组件的 DOM 节点内部。这可能会带来一些问题：样式层叠问题，z-index层级问题，可访问性与第三方库集成

*/


/* Suspense
等待异步组件时渲染一些额外内容，让应用有更好的用户体验
使用步骤：
异步导入组件
使用Suspense包裹组件，并配置号default和fallback
import { defineAsyncComponent, Suspense } from 'vue
const Child = defineAsyncComponent(() => import('./Child.vue'))


示例：
<Suspense>
  <template #default>
    <Child />
  </template>
  <template #fallback>
    <div>加载中...</div>
  </template>
</Suspense>

默认插槽（#default）：当异步组件加载完成时渲染的内容
后备插槽（#fallback）：当异步组件加载过程中渲染的内容

应用场景
1.带有异步 setup()钩子的组件：这包括了使用 <script setup>且包含顶层 await表达式的组件
2.异步组件：通过 defineAsyncComponent定义的组件默认就是 "suspensible"（可悬挂的），其加载状态将由上级的 <Suspense>控制
*/


/* ---------------------------------------------语法糖-------------------------------------------------  */

/* v-bind
在 Vue 的模板中，我们使用双大括号 {{ }}可以进行文本插值，但它不能用在 HTML 标签的属性上。这时就需要 v-bind来帮忙
简单来说：v-bind让 HTML 属性（如 id、class、src、href等）的值变得“动态”和“灵活”，这个值可以来自 Vue 实例（或组件）的响应式数据
示例：
<template>
  <img v-bind:src="imageSrc" v-bind:alt="imageAlt">
  <a v-bind:href="url">链接</a>
</template>

<script setup>
import { ref } from 'vue';

const imageSrc = ref('/path/to/image.jpg');
const imageAlt = ref('一张美丽的图片');
const url = ref('https://vuejs.org');
</script>
*/